--- 'Monad' transformer for 'Either'
package frege.control.monad.trans.EitherT where


import Data.Monoid
import frege.control.monad.trans.MonadTrans
import frege.control.monad.trans.MonadIO

data EitherT l m a = EitherT { run :: m (Either l a) }


left :: Monad m => l -> EitherT l m a
left = EitherT . return . Left

instance Functor f => Functor (EitherT l f) where
    fmap :: Functor γ => (δ -> α) -> EitherT β γ δ -> EitherT β γ α
    fmap f = EitherT . fmap (fmap f) . EitherT.run

instance Monad m => Monad (EitherT l m) where
    pure = EitherT . pure . Right
    EitherT x >>= f = EitherT  do
        res <- x
        case res of
          Right r -> EitherT.run  (f r)
          Left l -> return (Left l)

instance MonadTrans (EitherT l) where
    lift = EitherT . liftM Right 

instance MonadIO m => MonadIO (EitherT l m) where
  liftIO = lift . liftIO

instance (MonadPlus m) => MonadPlus (EitherT l m) where
    mzero = EitherT mzero
    e1 `mplus` e2 = EitherT $ e1.run `mplus` e2.run 

instance (MonadAlt m) => MonadAlt (EitherT l m) where
    pzero = EitherT pzero
    e1 <|> e2 = EitherT $ e1.run <|> e2.run
    e1 <+> e2 = EitherT $ e1.run <+> e2.run

instance (Monad m, Monoid a) => Monoid (EitherT l m a) where
    mempty = pure mempty
    mappend = liftA2 mappend
  